home *** CD-ROM | disk | FTP | other *** search
/ Shareware Super Platinum 8 / Shareware Super Platinum 8.iso / mac / EDITORS / AE170.ZIP;1 / AE1.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-08-12  |  26.7 KB  |  766 lines

  1. UNIT AE1 ;
  2.  
  3. {$R-}
  4. {$B-}
  5. {$I-}
  6. {$S+}
  7. {$V-}
  8.  
  9. {-----------------------------------------------------------------------------}
  10. { This unit contains all basic procedures                                     }
  11. {-----------------------------------------------------------------------------}
  12.  
  13. INTERFACE
  14.  
  15. USES Crt, Dos, AE0 ;
  16.  
  17. FUNCTION UpperCase (S : STRING) : STRING ;
  18. FUNCTION WordToString (Num : WORD ; Len : INTEGER) : STRING ;
  19. FUNCTION Wildcarded (Name : PathStr) : BOOLEAN ;
  20. FUNCTION Exists (FileName : PathStr) : BOOLEAN ;
  21. PROCEDURE MoveToScreen (VAR Source, Dest ; Len : WORD) ;
  22. PROCEDURE MoveFromScreen (VAR Source, Dest ; Len : WORD) ;
  23. PROCEDURE SaveArea (X1, Y1, X2, Y2 : WORD ; VAR MemPtr : ScreenBlockPtr) ;
  24. PROCEDURE RestoreArea (X1, Y1, X2, Y2 : WORD ; VAR MemPtr : ScreenBlockPtr) ;
  25. FUNCTION Grow (Index : WORD ; Chars : WORD) : BOOLEAN ;
  26. PROCEDURE Shrink (Index : WORD ; Chars : WORD) ;
  27. FUNCTION GetCursor : BYTE ;
  28. PROCEDURE SetCursor (Cursor : BYTE) ;
  29. PROCEDURE CursorTo (X, Y : BYTE) ;
  30. PROCEDURE WarningBeep ;
  31. FUNCTION ReadKeyNr : WORD ;
  32. PROCEDURE SetBottomLine (LineText : STRING) ;
  33. PROCEDURE Message (Contents : STRING) ;
  34. PROCEDURE ErrorMessage (ErrorNr : BYTE) ;
  35. PROCEDURE Pause ;
  36. PROCEDURE CheckDiskError ;
  37. PROCEDURE PutFrame (X1, Y1, X2, Y2 : BYTE ; Border : STRING) ;
  38. PROCEDURE ClearArea (X1, Y1, X2, Y2 : BYTE) ;
  39. PROCEDURE ClearCurrentWs ;
  40. PROCEDURE ClearKeyBuffer ;
  41. PROCEDURE CheckEsc ;
  42. PROCEDURE CreateHistory (VAR Hp : HistPtr ; LineLen : BYTE) ;
  43. PROCEDURE AddToHistory (Hp : HistPtr ; S : STRING) ;
  44. FUNCTION CurrentHistLine (Hp : HistPtr) : STRING ;
  45. FUNCTION NextHistLine (Hp : HistPtr) : STRING ;
  46. FUNCTION PrevHistLine (Hp : HistPtr) : STRING ;
  47. FUNCTION LeftMargin (VAR P : Position) : WORD ;
  48. {$IFDEF DEVELOP }
  49. PROCEDURE GetMem (VAR P : pointer ; Size : WORD ) ;
  50. {$ENDIF }
  51.  
  52. IMPLEMENTATION
  53.  
  54. {-----------------------------------------------------------------------------}
  55. { Converts all lower case letters in a string to upper case.                  }
  56. {-----------------------------------------------------------------------------}
  57.  
  58. FUNCTION UpperCase (S : STRING) : STRING ;
  59.  
  60. VAR Counter : WORD ;
  61.  
  62. BEGIN
  63. FOR Counter := 1 TO LENGTH (S) DO S [Counter] := UPCASE (S [Counter]) ;
  64. UpperCase := S ;
  65. END ;
  66.  
  67. {-----------------------------------------------------------------------------}
  68. { Converts an expression of type word to a string                             }
  69. { if Len < 0 then string is adjusted to the left; string length is <Len>      }
  70. { if Len > 0 then string is adjusted to the right; string length is <-Len>    }
  71. { if Len = 0 then string is not adjusted; string has minimum length           }
  72. {-----------------------------------------------------------------------------}
  73.  
  74. FUNCTION WordToString (Num : WORD ; Len : INTEGER) : STRING ;
  75.  
  76. VAR S : STRING [5] ;
  77.  
  78. BEGIN
  79. IF Len > 0
  80.    THEN STR (Num : Len, S)
  81.    ELSE BEGIN
  82.         STR (Num, S) ;
  83.         Len := - Len ;
  84.         IF (Len > 0) AND (LENGTH (S) < Len)
  85.            THEN BEGIN
  86.                 FILLCHAR (S [LENGTH (S) + 1], Len - LENGTH (S), ' ') ;
  87.                 S [0] := CHR (Len) ;
  88.                 END ;
  89.         END ;
  90. WordToString := S ;
  91. END ;
  92.  
  93. {-----------------------------------------------------------------------------}
  94. { Deletes all spaces on the left of a string.                                 }
  95. {-----------------------------------------------------------------------------}
  96.  
  97. FUNCTION TrimLeft (S : STRING) : STRING ;
  98.  
  99. BEGIN
  100. WHILE (LENGTH (S) > 0) AND (S [1] = ' ') DO DELETE (S, 1, 1) ;
  101. TrimLeft := S ;
  102. END ;
  103.  
  104. {-----------------------------------------------------------------------------}
  105. { Indicates whether a filename contains wildcard characters                   }
  106. {-----------------------------------------------------------------------------}
  107.  
  108. FUNCTION Wildcarded (Name : PathStr) : BOOLEAN ;
  109.  
  110. BEGIN
  111. Wildcarded := (POS ('*', Name) <> 0) OR (POS ('?', Name) <> 0) ;
  112. END ;
  113.  
  114. {-----------------------------------------------------------------------------}
  115. { Returns True if the file <FileName> exists, False otherwise.                }
  116. {-----------------------------------------------------------------------------}
  117.  
  118. FUNCTION Exists (FileName : PathStr) : BOOLEAN ;
  119.  
  120. VAR SR : SearchRec ;
  121.  
  122. BEGIN
  123. FINDFIRST (FileName, ReadOnly + Hidden + SysFile, SR) ;
  124. Exists := (DosError = 0) AND (NOT Wildcarded (Filename) ) ;
  125. END ;
  126.  
  127. {-----------------------------------------------------------------------------}
  128. { Moves <Len> bytes of memory to screen memory.                               }
  129. { From the TCALC spreadsheet program delivered with every copy of Turbo       }
  130. { Pascal 5.5                                                                  }
  131. {-----------------------------------------------------------------------------}
  132.  
  133. PROCEDURE MoveToScreen (VAR Source, Dest ; Len : WORD) ;
  134.  
  135. EXTERNAL ;
  136.  
  137. {-----------------------------------------------------------------------------}
  138. { Moves <Len> bytes of screen memory to memory.                               }
  139. { From the TCALC spreadsheet program delivered with every copy of Turbo       }
  140. { Pascal 5.5                                                                  }
  141. {-----------------------------------------------------------------------------}
  142.  
  143. PROCEDURE MoveFromScreen (VAR Source, Dest ; Len : WORD) ;
  144.  
  145. EXTERNAL ;
  146.  
  147. {$L TCMVSMEM.OBJ }
  148.  
  149. {-----------------------------------------------------------------------------}
  150. { Saves the contents of a rectangular part of the screen to memory.           }
  151. { Upper left corner is (X1,Y1), lower right is (X2,Y2)                        }
  152. { Also claims the amount of memory needed.                                    }
  153. {-----------------------------------------------------------------------------}
  154.  
  155. PROCEDURE SaveArea (X1, Y1, X2, Y2 : WORD ; VAR MemPtr : ScreenBlockPtr) ;
  156.  
  157. VAR LineLen : BYTE;
  158.     Index : WORD;
  159.     Counter : BYTE;
  160.  
  161. BEGIN
  162. LineLen := X2 - X1 + 1;
  163. GETMEM (POINTER(MemPtr), LineLen * (Y2 - Y1 + 1) * 2) ;
  164. Index := 1 ;
  165. FOR Counter := Y1 TO Y2 DO
  166.     BEGIN
  167.     MoveFromScreen (DisplayPtr^ [Counter, X1], MemPtr^ [Index], LineLen * 2);
  168.     INC (Index, LineLen)
  169.     END;
  170. END;
  171.  
  172. {-----------------------------------------------------------------------------}
  173. { Reverse of SaveArea                                                         }
  174. {-----------------------------------------------------------------------------}
  175.  
  176. PROCEDURE RestoreArea (X1, Y1, X2, Y2 : WORD ; VAR MemPtr : ScreenBlockPtr) ;
  177.  
  178. VAR LineLen : BYTE;
  179.     Index : WORD;
  180.     Counter : BYTE;
  181.  
  182. BEGIN
  183. LineLen := X2 - X1 + 1;
  184. Index := 1;
  185. FOR Counter := Y1 TO Y2 DO
  186.     BEGIN
  187.     MoveToScreen (MemPtr^ [Index], DisplayPtr^ [Counter, X1], LineLen * 2);
  188.     INC (Index, LineLen)
  189.     END;
  190. FREEMEM (MemPtr, LineLen * (Y2 - Y1 + 1) * 2) ;
  191. END;
  192.  
  193. {-----------------------------------------------------------------------------}
  194. { Expands the text in the buffer of the current workspace at position         }
  195. { <Index> by <Chars> characters. Function result is False if there is not     }
  196. { enough space left, True otherwise.                                          }
  197. { Index values of Mark and in position stack are adapted                      }
  198. {-----------------------------------------------------------------------------}
  199.  
  200. FUNCTION Grow (Index : WORD ; Chars : WORD) : BOOLEAN ;
  201.  
  202. VAR Counter : BYTE ;
  203.  
  204. BEGIN
  205. WITH CurrentWs DO
  206.      IF Chars > (WsBufSize - BufferSize)
  207.         THEN BEGIN
  208.              { not enough space }
  209.              ErrorMessage (1) ;
  210.              Grow := FALSE ;
  211.              END
  212.         ELSE BEGIN
  213.              { move rest of text forward }
  214.              MOVE (Buffer^ [Index], Buffer^ [Index + Chars], BufferSize - Index + 1) ;
  215.              INC (BufferSize, Chars) ;
  216.              { adapt Mark and position stack }
  217.              IF MARK >= Index THEN INC (MARK, Chars) ;
  218.              FOR Counter := 1 TO PosStackpointer DO
  219.                  BEGIN
  220.                  IF PosStack [Counter] >= Index
  221.                     THEN INC (PosStack [Counter], Chars) ;
  222.                  END ;
  223.              ChangesMade := TRUE ;
  224.              Grow := TRUE ;
  225.              END ;
  226. END ;
  227.  
  228. {-----------------------------------------------------------------------------}
  229. { Deletes <Chars> characters from the buffer in the current workspace,        }
  230. { starting on position <Index>.                                               }
  231. { Index values of Mark and in position stack are adapted                      }
  232. {-----------------------------------------------------------------------------}
  233.  
  234. PROCEDURE Shrink (Index : WORD ; Chars : WORD) ;
  235.  
  236. VAR Counter : WORD ;
  237.  
  238. BEGIN
  239. WITH CurrentWs DO
  240.      BEGIN
  241.      { move rest of text backward }
  242.      MOVE (Buffer^ [Index + Chars], Buffer^ [Index], BufferSize - (Index + Chars) + 1) ;
  243.      DEC (BufferSize, Chars) ;
  244.      { adapt Mark }
  245.      IF (MARK >= Index)
  246.         THEN BEGIN
  247.              IF (MARK < (Index + Chars) )
  248.                 THEN MARK := Inactive
  249.                 ELSE DEC (MARK, Chars) ;
  250.              END ;
  251.      { adapt position stack }
  252.      FOR Counter := 1 TO PosStackpointer DO
  253.          IF (PosStack [Counter] >= Index)
  254.             THEN BEGIN
  255.                  IF (PosStack [Counter] < (Index + Chars) )
  256.                     THEN PosStack [Counter] := Index
  257.                     ELSE DEC (PosStack [Counter], Chars) ;
  258.                  END ;
  259.      ChangesMade := TRUE ;
  260.      END ;
  261. END ;
  262.  
  263. {-----------------------------------------------------------------------------}
  264. { Returns the current cursor type                                             }
  265. {-----------------------------------------------------------------------------}
  266.  
  267. FUNCTION GetCursor : BYTE ;
  268.  
  269. VAR Reg : REGISTERS ;
  270.  
  271. BEGIN
  272. WITH Reg DO
  273.      BEGIN
  274.      AH := 3 ;
  275.      BH := 0 ;
  276.      { call BIOS interrupt }
  277.      INTR ($10, Reg) ;
  278.      CASE CX OF
  279.           $0607, $0B0C : GetCursor := UnderLineCursor ;
  280.           $0507, $090C : GetCursor := HalfBlockCursor ;
  281.           $0807, $0D0C : GetCursor := BlockCursor ;
  282.           $2000       : GetCursor := Inactive ;
  283.           $2001       : GetCursor := NoBlinkCursor ;
  284.           ELSE          GetCursor := UnderLineCursor ;
  285.           END ; { of case }
  286.      END ; { of with }
  287. END ;
  288.  
  289. {-----------------------------------------------------------------------------}
  290. { Sets a new cursor                                                           }
  291. {-----------------------------------------------------------------------------}
  292.  
  293. PROCEDURE SetCursor (Cursor : BYTE) ;
  294.  
  295. VAR Reg : REGISTERS ;
  296.     ScrEl : ScreenElement ;
  297.  
  298. BEGIN
  299. WITH Reg DO
  300.      BEGIN
  301.      AH := 1 ;
  302.      BH := 0 ;
  303.      { monochrome and color cards require different settings for cursor shape }
  304.      CASE Cursor OF
  305.           Inactive        : CX := $2000 ;
  306.           UnderLineCursor : IF Colorcard THEN CX := $0607 ELSE CX := $0B0C ;
  307.           HalfBlockCursor : IF Colorcard THEN CX := $0507 ELSE CX := $090C;
  308.           BlockCursor     : IF Colorcard THEN CX := $0807 ELSE CX := $0D0C ;
  309.           NoBlinkCursor   : CX := $2001 ;
  310.           END ; { of case }
  311.      { call BIOS interrupt }
  312.      INTR ($10, Reg) ;
  313.      END ; { with }
  314. IF Cursor = NoBlinkCursor
  315.    THEN BEGIN
  316.         { put NoBlinkCursor on new position }
  317.         ScrEl := ScreenElement (DisplayPtr^ [WHEREY, WHEREX]) ;
  318.         { set cursor attribute }
  319.         WITH ScreenColorArray [Config.Setup.ScreenColors] DO
  320.              IF WHEREY = LinesOnScreen
  321.                 THEN ScrEl.Attribute := CursorAttr
  322.                 ELSE ScrEl.Attribute := StatusCursorAttr ;
  323.         DisplayPtr^ [WHEREY, WHEREX] := WORD (ScrEl) ;
  324.         END ;
  325. END ;
  326.  
  327. {-----------------------------------------------------------------------------}
  328. { Positions the cursor at (X,Y)                                               }
  329. {-----------------------------------------------------------------------------}
  330.  
  331. PROCEDURE CursorTo (X, Y : BYTE) ;
  332.  
  333. VAR ScrEl : ScreenElement ;
  334.  
  335. BEGIN
  336. GOTOXY (X, Y) ;
  337. IF Config.Setup.CursorType = NoBlinkCursor
  338.    THEN BEGIN
  339.         { put NoBlinkCursor on new position }
  340.         ScrEl := ScreenElement (DisplayPtr^ [Y, X]) ;
  341.         { set cursor attribute }
  342.         WITH ScreenColorArray [Config.Setup.ScreenColors] DO
  343.              IF WHEREY = LinesOnScreen
  344.                 THEN ScrEl.Attribute := StatusCursorAttr
  345.                 ELSE ScrEl.Attribute := CursorAttr ;
  346.         DisplayPtr^ [Y, X] := WORD (ScrEl) ;
  347.         END ;
  348. END ;
  349.  
  350. {-----------------------------------------------------------------------------}
  351. { Produces a low beep trough the speaker, unless inhibited by Setup           }
  352. {-----------------------------------------------------------------------------}
  353.  
  354. PROCEDURE WarningBeep ;
  355.  
  356. BEGIN
  357. IF Config.Setup.SoundBell
  358.    THEN BEGIN
  359.         SOUND (110) ;
  360.         DELAY (100) ;
  361.         NOSOUND ;
  362.         END ;
  363. END ;
  364.  
  365. {-----------------------------------------------------------------------------}
  366. { Waits until a key on the keyboard is pressed and returns its key number.    }
  367. { Control keys (cursor keys, function keys etc.) are translated to numbers    }
  368. { above 255.                                                                  }
  369. {-----------------------------------------------------------------------------}
  370.  
  371. FUNCTION ReadKeyNr : WORD ;
  372.  
  373. VAR Regs : REGISTERS ;
  374.  
  375. BEGIN
  376. WITH Regs DO
  377.      BEGIN
  378.      AH := 0 ;
  379.      INTR ($16, Regs) ;
  380.      { AL now contains the ASCII value of the key, AH the scan code }
  381.      CASE AL OF
  382.            0 : IF AH = 3  THEN ReadKeyNr := 0    { ^@ }
  383.                           ELSE ReadKeyNr := 256 + AH ;
  384.            8 : IF AH = 14 THEN ReadKeyNr := BkspKey
  385.                           ELSE ReadKeyNr := 8 ;  { ^H }
  386.            9 : IF AH = 15 THEN ReadKeyNr := TabKey
  387.                           ELSE ReadKeyNr := 9 ;  { ^I }
  388.           10 : IF AH = 28 THEN ReadKeyNr := CtrlReturnKey
  389.                           ELSE ReadKeyNr := 10 ; { ^J }
  390.           13 : IF AH = 28 THEN ReadKeyNr := ReturnKey
  391.                           ELSE ReadKeyNr := 13 ; { ^M }
  392.           27 : IF AH = 1  THEN ReadKeyNr := EscapeKey
  393.                           ELSE ReadKeyNr := 27 ; { ^[ }
  394.           ELSE ReadKeyNr := AL ;
  395.           END ; { of case }
  396.      END ; { of with }
  397. END ;
  398.  
  399. {-----------------------------------------------------------------------------}
  400. { Puts a line of text on the last line of the screen.                         }
  401. { Writes directly into video memory.                                          }
  402. {-----------------------------------------------------------------------------}
  403.  
  404. PROCEDURE SetBottomLine (LineText : STRING) ;
  405.  
  406. VAR ScrEl : ScreenElement ;
  407.     ScrElPtr : ScreenElementPtr ;
  408.     Col : BYTE ;
  409.  
  410. BEGIN
  411. ScrElPtr := ScreenElementPtr (StatusLinePtr) ;
  412. { set attribute }
  413. ScrEl.Attribute := ScreenColorArray [Config.Setup.ScreenColors].StatusAttr ;
  414. { fill first part of status line with LineText }
  415. FOR Col := 1 TO LENGTH (LineText) DO
  416.     BEGIN
  417.     ScrEl.Contents := LineText [Col] ;
  418.     ScrElPtr.Ref^ := ScrEl ;
  419.     INC (ScrElPtr.OFS, 2) ;
  420.     END ;
  421. { fill rest of status line with spaces }
  422. ScrEl.Contents := ' ' ;
  423. FOR Col := (LENGTH (LineText) + 1) TO ColsOnScreen DO
  424.     BEGIN
  425.     ScrElPtr.Ref^ := ScrEl ;
  426.     INC (ScrElPtr.OFS, 2) ;
  427.     END ;
  428. END ;
  429.  
  430. {-----------------------------------------------------------------------------}
  431. { Produces a message on the last line of the screen and sets MessageRead      }
  432. {-----------------------------------------------------------------------------}
  433.  
  434. PROCEDURE Message (Contents : STRING) ;
  435.  
  436. BEGIN
  437. SetBottomLine (Contents) ;
  438. MessageRead := (LENGTH (Contents) = 0) ;
  439. END ;
  440.  
  441. {-----------------------------------------------------------------------------}
  442. { Produces an error beep (if allowed by Setup), writes an error message       }
  443. { corresponding to the error number, on the last screen line and waits        }
  444. { until the Escape key is pressed.                                            }
  445. { If any macros are running, they are canceled.                               }
  446. {-----------------------------------------------------------------------------}
  447.  
  448. PROCEDURE ErrorMessage (ErrorNr : BYTE) ;
  449.  
  450. VAR ErrorText : STRING [ColsOnScreen] ;
  451.  
  452. BEGIN
  453. IF Config.Setup.SoundBell
  454.    THEN BEGIN
  455.         SOUND (880) ;
  456.         DELAY (100) ;
  457.         NOSOUND ;
  458.         END ;
  459. CASE ErrorNr OF
  460.        1 : ErrorText := 'Not enough memory' ;
  461.        4 : ErrorText := 'Block too large for paste buffer' ;
  462.        5 : ErrorText := 'No block defined' ;
  463.        6 : ErrorText := 'Maximum macro length reached. End of define mode' ;
  464.        7 : ErrorText := 'File too large. Only partially read' ;
  465.        8 : ErrorText := 'File not found' ;
  466.        9 : ErrorText := 'Cyclic macro definition. Key ignored' ;
  467.       10 : ErrorText := 'Too many macros nested. Execution canceled' ;
  468.       11 : ErrorText := 'Word wrap mode must be on to do this' ;
  469.       12 : ErrorText := 'Position stack full' ;
  470.       13 : ErrorText := 'Position stack empty' ;
  471.       14 : CASE DosError OF
  472.                 2  : ErrorText := 'Can not find COMMAND.COM ' ;
  473.                 8  : ErrorText := 'Not enough memory to execute DOS command' ;
  474.                 ELSE ErrorText := 'DOS error ' + WordToString (DosError, 2) ;
  475.                 END ; { of case }
  476.       15 : ErrorText := 'String not found' ;
  477.       16 : ErrorText := 'Illegal file name' ;
  478.       17 : CASE DiskError OF
  479.                 2   : ErrorText := 'File not found' ;
  480.                 3   : ErrorText := 'Path not found' ;
  481.                 5   : ErrorText := 'File access denied' ;
  482.                 100 : ErrorText := 'Disk read error' ;
  483.                 101 : ErrorText := 'Disk write error' ;
  484.                 103 : ErrorText := 'File not open' ;
  485.                 150 : ErrorText := 'Disk is write-protected' ;
  486.                 152 : ErrorText := 'Drive not ready' ;
  487.                 159 : ErrorText := 'Printer out of paper' ;
  488.                 160 : ErrorText := 'Device write fault' ;
  489.                 ELSE  ErrorText := 'I/O error ' + WordToString (DiskError, 0) ;
  490.                 END ; { of case }
  491.       18 : ErrorText := 'Macro execution interrupted' ;
  492.       19 : ErrorText := 'Bad or incompatible configuration file. Using default' ;
  493.       20 : ErrorText := 'Please enter a number' ;
  494.       21 : ErrorText := 'Number is too low' ;
  495.       22 : ErrorText := 'Number is too high' ;
  496.       23 : ErrorText := 'Bad or incompatible work file' ;
  497.       END ; { of case }
  498. SetBottomLine (ErrorText + ' (press Esc)') ;
  499. REPEAT UNTIL ReadKeyNr = EscapeKey ;
  500. IF MacroStackpointer <> Inactive
  501.    THEN BEGIN
  502.         MacroStackpointer := Inactive ;
  503.         Message ('Macro execution canceled') ;
  504.         END
  505.    ELSE Message ('') ;
  506. END ;
  507.  
  508. {-----------------------------------------------------------------------------}
  509. { Like the DOS batch command, Pause displays the message 'Press any key to    }
  510. { continue' and then waits until a key is pressed.                            }
  511. {-----------------------------------------------------------------------------}
  512.  
  513. PROCEDURE Pause ;
  514.  
  515. VAR DummyKey : WORD ;
  516.  
  517. BEGIN
  518. SetBottomLine ('Press any key to continue') ;
  519. DummyKey := ReadKeyNr ;
  520. EscPressed := (DummyKey = EscapeKey) ;
  521. SetBottomLine ('') ;
  522. END ;
  523.  
  524. {-----------------------------------------------------------------------------}
  525. { Reads the result of the last I/O operation into the DiskError variable      }
  526. { and produces an error message if an error has occurred.                     }
  527. {-----------------------------------------------------------------------------}
  528.  
  529. PROCEDURE CheckDiskError ;
  530.  
  531. BEGIN
  532. DiskError := IORESULT ;
  533. IF DiskError <> 0 THEN ErrorMessage (17) ;
  534. END ;
  535.  
  536. {-----------------------------------------------------------------------------}
  537. { Draws a frame on the text screen between (X1,Y1) and (X2,Y2)                }
  538. {-----------------------------------------------------------------------------}
  539.  
  540. PROCEDURE PutFrame (X1, Y1, X2, Y2 : BYTE ; Border : STRING) ;
  541.  
  542. VAR i : BYTE ;
  543.  
  544. BEGIN
  545. CursorTo (X1, Y1) ; WRITE (Border [1]) ; { upper left corner }
  546. FOR i := SUCC (X1) TO PRED (X2) DO WRITE (Border [2]) ; { upper side }
  547. WRITE (Border [3]) ; { upper right corner }
  548. FOR i := SUCC (Y1) TO PRED (Y2) DO
  549.     BEGIN
  550.     CursorTo (X1, i) ; WRITE (Border [8]) ; { left side }
  551.     CursorTo (X2, i) ; WRITE (Border [4]) ; { right side }
  552.     END ;
  553. CursorTo (X1, Y2) ; WRITE (Border [7]) ; { lower right corner }
  554. FOR i := SUCC (X1) TO PRED (X2) DO WRITE (Border [6]) ; { lower side }
  555. WRITE (Border [5]) ; { lower left corner }
  556. END ;
  557.  
  558. {-----------------------------------------------------------------------------}
  559. { Clears a rectangular screen area between (X1,Y1) and (X2,Y2).               }
  560. {-----------------------------------------------------------------------------}
  561.  
  562. PROCEDURE ClearArea (X1, Y1, X2, Y2 : BYTE) ;
  563.  
  564. VAR OldWindMax, OldWindMin : WORD ;
  565.  
  566. BEGIN
  567. OldWindMax := WindMax ;
  568. OldWindMin := WindMin ;
  569. WINDOW (X1, Y1, X2, Y2) ;
  570. CLRSCR ;
  571. WINDOW (LO (OldWindMin) + 1, HI (OldWindMin) + 1,
  572.         LO (OldWindMax) + 1, HI (OldWindMax) + 1) ;
  573. END ;
  574.  
  575. {-----------------------------------------------------------------------------}
  576. { Clears the current workspace, resetting all variables.                      }
  577. {-----------------------------------------------------------------------------}
  578.  
  579. PROCEDURE ClearCurrentWs ;
  580.  
  581. BEGIN
  582. WITH Workspace [CurrentWsnr] DO
  583.      BEGIN
  584.      Name := '' ;
  585.      ChangesMade := FALSE ;
  586.      GETTIME (LastTimeSaved [1], LastTimeSaved [2],
  587.               LastTimeSaved [3], LastTimeSaved [4]) ;
  588.      CurPos.Index := 1 ;
  589.      CurPos.Linenr := 1 ;
  590.      CurPos.Colnr := 1 ;
  591.      MARK := Inactive ;
  592.      FirstVisibleLine := CurPos ;
  593.      FirstScreenCol := 1 ;
  594.      VirtualColnr := 1 ;
  595.      Buffer^ [1] := EF ;
  596.      Buffersize := 1 ;
  597.      PosStackPointer := Inactive ;
  598.      END ;
  599. { make copy of current workspace equal to original }
  600. CurrentWs := Workspace [CurrentWsnr] ;
  601. END ;
  602.  
  603. {-----------------------------------------------------------------------------}
  604. { Clears the keys in the keyboard buffer.                                     }
  605. {-----------------------------------------------------------------------------}
  606.  
  607. PROCEDURE ClearKeyBuffer ;
  608.  
  609. VAR DummyKey : CHAR ;
  610.  
  611. BEGIN
  612. WHILE KEYPRESSED DO DummyKey := READKEY ;
  613. END ;
  614.  
  615. {-----------------------------------------------------------------------------}
  616. { Checks if the Escape key has been pressed                                   }
  617. {-----------------------------------------------------------------------------}
  618.  
  619. PROCEDURE CheckEsc ;
  620.  
  621. BEGIN
  622. EscPressed := FALSE ;
  623. WHILE KEYPRESSED DO
  624.       IF READKEY = ESC THEN EscPressed := TRUE ;
  625. END ;
  626.  
  627. {-----------------------------------------------------------------------------}
  628. { Creates an empty history with lines of <LineLen> chars long                 }
  629. {-----------------------------------------------------------------------------}
  630.  
  631. PROCEDURE CreateHistory (VAR Hp : HistPtr ; LineLen : BYTE) ;
  632.  
  633. VAR i : BYTE ;
  634.  
  635. BEGIN
  636. NEW (Hp) ;
  637. FOR i := 1 TO MaxHistLength DO
  638.     GETMEM (POINTER(Hp^.LINE [i]), LineLen + 1) ;
  639. Hp^.MaxLineLen := LineLen ;
  640. Hp^.Len := 0 ;
  641. Hp^.CurLine := 0 ;
  642. END ;
  643.  
  644. {-----------------------------------------------------------------------------}
  645. { Adds a new string to a history, unless already present                      }
  646. {-----------------------------------------------------------------------------}
  647.  
  648. PROCEDURE AddToHistory (Hp : HistPtr ; S : STRING) ;
  649.  
  650. VAR i,j : BYTE ;
  651.  
  652. BEGIN
  653. WITH Hp^ DO
  654.      BEGIN
  655.      { check if line already present in history }
  656.      i := 1 ;
  657.      WHILE (i < Len ) AND (S <> LINE [i]^) DO
  658.            INC (i) ;
  659.      IF (Len > 0) AND (S = LINE[i]^)
  660.         THEN BEGIN
  661.              { move this line to top of history }
  662.              FOR j := i TO (Len-1) DO
  663.                  LINE[j]^ := LINE[j+1]^ ;
  664.              LINE[Len]^ := S ;
  665.              END
  666.         ELSE BEGIN
  667.              { add line to end of history }
  668.              IF Len < MaxHistLength
  669.                 THEN { expand history }
  670.                      INC (Len)
  671.                 ELSE { history full: shift lines, losing the oldest one }
  672.                      FOR i := 1 TO (Len - 1) DO
  673.                          LINE [i]^ := LINE [i + 1]^ ;
  674.              LINE [Len]^ := COPY (S, 1, MaxLineLen) ;
  675.              END ;
  676.      { set current line so that next PrevHistLine returns this line }
  677.      CurLine := 0 ;
  678.      END ;
  679. END ;
  680.  
  681. {-----------------------------------------------------------------------------}
  682. { Returns the current history line                                            }
  683. {-----------------------------------------------------------------------------}
  684.  
  685. FUNCTION CurrentHistLine (Hp : HistPtr) : STRING ;
  686.  
  687. BEGIN
  688. WITH Hp^ DO
  689.      IF (Len = 0) OR (CurLine = 0)
  690.         THEN CurrentHistLine := ''
  691.         ELSE CurrentHistLine := LINE [CurLine]^ ;
  692. END ;
  693.  
  694. {-----------------------------------------------------------------------------}
  695. { Returns the history line above the current one                              }
  696. {-----------------------------------------------------------------------------}
  697.  
  698. FUNCTION NextHistLine (Hp : HistPtr) : STRING ;
  699.  
  700. BEGIN
  701. WITH Hp^ DO
  702.      BEGIN
  703.      IF CurLine = Len
  704.         THEN CurLine := 0
  705.         ELSE INC (CurLine) ;
  706.      NextHistLine := CurrentHistLine (Hp) ;
  707.      END ;
  708. END ;
  709.  
  710. {-----------------------------------------------------------------------------}
  711. { Returns the history line below the current one                              }
  712. {-----------------------------------------------------------------------------}
  713.  
  714. FUNCTION PrevHistLine (Hp : HistPtr) : STRING ;
  715.  
  716. BEGIN
  717. WITH Hp^ DO
  718.      BEGIN
  719.      IF CurLine = 0
  720.         THEN CurLine := Len
  721.         ELSE DEC (CurLine) ;
  722.      PrevHistLine := CurrentHistLine (Hp) ;
  723.      END ;
  724. END ;
  725.  
  726. {-----------------------------------------------------------------------------}
  727. { Determines the left margin of the current line. Position P must be after    }
  728. { the first non-space, otherwise the result is 1.                             }
  729. {-----------------------------------------------------------------------------}
  730.  
  731. FUNCTION LeftMargin (VAR P : Position) : WORD ;
  732.  
  733. VAR Counter : WORD ;
  734.  
  735. BEGIN
  736. WITH CurrentWs DO
  737.      BEGIN
  738.      { look for first non-space on current line }
  739.      Counter := 1 ;
  740.      WHILE (Buffer^ [P.Index - P.Colnr + Counter] = ' ') AND
  741.            (Counter <= P.Colnr) DO
  742.            INC (Counter) ;
  743.      IF (Counter > P.Colnr)
  744.         THEN LeftMargin := 1
  745.         ELSE LeftMargin := Counter ;
  746.      END ; { of with }
  747. END ;
  748.  
  749. {-----------------------------------------------------------------------------}
  750. { GetMem is redirected, to keep track of available memory.                    }
  751. {-----------------------------------------------------------------------------}
  752.  
  753. {$IFDEF DEVELOP }
  754. PROCEDURE GetMem (VAR P : pointer ; Size : WORD ) ;
  755.  
  756. BEGIN
  757. System.GetMem (P, Size) ;
  758. IF MEMAVAIL < MinMemAvail
  759.    THEN MinMemAvail := MEMAVAIL ;
  760. END ;
  761. {$ENDIF }
  762.  
  763. {-----------------------------------------------------------------------------}
  764.  
  765. END.
  766.